package com.jule.teenpatti.game.model;

import com.jule.teenpatti.base.dao.bean.User;
import com.jule.teenpatti.base.enums.RedisConst;
import com.jule.teenpatti.base.enums.RoleType;
import com.jule.teenpatti.base.model.RoomTableRelationModel;
import com.jule.teenpatti.game.play.AbstractTable;
import com.jule.teenpatti.game.service.LogService;
import com.jule.teenpatti.game.service.MoneyService;
import com.jule.teenpatti.game.utils.NumberUtils;
import com.google.common.primitives.Ints;
import com.jule.core.jedis.StoredObjManager;
import com.jule.teenpatti.game.model.eenum.PlayTypeEnum;
import com.jule.teenpatti.game.model.eenum.PlayerStateEnum;
import com.jule.teenpatti.log.service.LogReasons;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 玩家信息
 * 描述：存储玩家在玩牌过程中的信息，如果一个玩家同时存在于两个桌子，那么将有两个相同playerId 的PlayerInfo，用tableId 进行区分存在于哪个桌子。
 */
@ToString
@Getter
@Setter
public class PlayerInfo {
    private final static Logger logger = LoggerFactory.getLogger(PlayerInfo.class);
    //add by gx 20181024 创立线程池，将写库和写日志操作通过线程池异步处理
    private final static ExecutorService executorService = Executors.newFixedThreadPool(8);

    private String playerId;//玩家id 和userI都一样
    private String roomId; //房间ID
    private String tableId;//用户桌子Id
    private String nickName;//显示的昵称
    private String icon; //玩家头像URL
    private boolean isOffLine = false;//是否已经掉线
    private int seatNum; //玩家座位号
    private double playScoreStore = 0;//玩家牌桌上积分的携带量

    //region 玩家在每一局的玩牌信息，每一局开始时重置掉
    private int isBlind; //是否盲牌1是0不是
    private int isDealer; //是否庄家
    private int isCurrActive; //是否当前行动玩家
    private double alreadyBetScore4Round; //当前轮次已经下注的积分量 (非累积值，每轮次重新计算)
    private PlayerStateEnum state;//玩家状态 0/旁观 1/入座 2/游戏中 3/已下注 4/已开牌
    public boolean haveXiQian;  //玩家有无喜钱
    public int fullPressureShow; //玩家是否是孤注一掷 0/不是 1/明牌孤注一掷 2/暗牌孤注一掷
    /**
     * 牌数组的下标
     */
    private int[] handCards = null;
    /**
     * 玩家是否可以与某个用户比边牌 0/不能 1/可以和所有人比 2/只能和上家比
     */
    private int isCanSideShow;
    private int isCanShow; //玩家是否可以比牌结算
    private double totalAlreadyBetScore4Hand; //当前牌局累积已下注金额，方便后续结算时判断用户输赢
    private double winLoseScore4Hand; //当前牌局输赢积分数量
    private int mixedCard; //玩家手上有几张癞子牌
    private List<Integer> mixedCardList; //记录玩家结算后的混牌id
    //endregion

    //region 此部分用于统计玩家玩牌时长，输赢积分
    private long joinTableTime; //加入牌桌时间
    private long leaveTableTime; //离开牌桌时间
    private long sitDownTime; //入座时间
    /**
     * 站起时间
     */
    private long standUpTime = 0L;
    private int totalPlayMinutes; //合计的玩牌时间（分钟数）
    /**
     * 入桌时带入积分及在桌内购入积分总合
     */
    private double totalTakeInScore = 0;
    private double totalWinLoseScore; //合计的输赢积分数
    private double biggestChipsWon = 0;
    /**
     * 最高的一次胜利
     * 2018-4-20 改成上一把输赢值
     */
    private double chipsWon = 0;
    private double chipsLost = 0;
    private double curChipsLose = 0;
    private WinLoseRecord handsWon = new WinLoseRecord();
    private boolean isWinner;//是否是本局赢家
    private boolean isTimeoutDiscard; //是否超时弃牌
    private int timeoutDiscardCount; //超时弃牌累计（连续超过三次则自动站起）
    /**
     * 旁观次数累计(连续超过三局自动离桌)
     */
    private int spectatorCount;
    //玩家sideshow选择玩家超时
    private boolean sideshowOvertime;
    private RoleType roleType;
    private boolean isJoker235;//判断玩家是否可以在特殊情况下更换牌型为235

    private boolean isStrategyRobot = false;

    public PlayerInfo(RoomTableRelationModel relationModel, String playerId, String nickName, String icon, RoleType roleType) {
        this.roomId = relationModel.getRoomId();
        this.tableId = relationModel.getTableId();
        this.playerId = playerId;
        this.nickName = nickName;
        this.icon = icon;
        this.isBlind = 1;
        setRoleType(roleType);
    }

    /**
     * 重置游戏中信息
     */
    public void resetGameingInfo() {
        //如果玩家不是自动弃牌则重置数据
        if (!this.isTimeoutDiscard) {
            this.timeoutDiscardCount = 0;
        }
        this.isBlind = 1;
        this.isDealer = 0;
        this.isCurrActive = 0;
        this.alreadyBetScore4Round = 0;
        this.totalAlreadyBetScore4Hand = 0;
        this.state = PlayerStateEnum.siteDown;
        this.handCards = null;
        this.isCanSideShow = 0;
        this.isCanShow = 0;
        this.winLoseScore4Hand = 0;
        this.mixedCard = 0;
        this.isWinner = false;
        this.isTimeoutDiscard = false;
        this.spectatorCount = 0;
        this.sideshowOvertime = false;
        this.haveXiQian = false;
        this.fullPressureShow = 0;
        this.isJoker235 = false;
    }

    public void sitDownAfterInit() {
        setBiggestChipsWon(0);
        setChipsWon(0);
        setChipsLost(0);
        setCurChipsLose(0);
        setTotalWinLoseScore(0);
        setState(PlayerStateEnum.siteDown); //修改玩家状态值
        handsWon.getLinkedDeque().clear();
    }

    /**
     * 获得手牌类型对象
     *
     * @return
     */
    public HandCardTypeModel getHandCardTypeModel(AbstractTable table) {
        //癞子牌玩法
        if (this.getHandCards() != null && (table.currPlayType() == PlayTypeEnum.hukam.getValue())) {
            List<Integer> list = new ArrayList<>(Ints.asList(this.getHandCards()));
            Iterator<Integer> it = list.iterator();
            while (it.hasNext()) {
                Integer ins = it.next();
                if ((ins % 13) == (table.getMixedCardId() % 13)) {
                    it.remove();
                }
            }
            this.setMixedCard(3 - list.size());
            this.setHandCards(list.stream().mapToInt(i -> i).toArray());
        }
        if (null != handCards) {
            HandCardTypeModel hctm;
            if(handCards.length == 3){
                hctm =  new HandCardTypeModel(handCards[0], handCards[1], handCards[2],this);
            }else{
                hctm = new HandCardTypeModel(this);
            }
            if(hctm.isSet()){ //如果玩家是三张则可以得到喜钱
                this.setHaveXiQian(true);
            }
            return hctm;
        }
        return null;
    }

    public String toSitDownString() {
        return "Player{" +
                "playerId='" + playerId + '\'' +
                ", roomId='" + roomId + '\'' +
                ", tableId='" + tableId + '\'' +
                ", seatNum=" + seatNum +
                ", playScoreStore=" + playScoreStore +
                ", state=" + state +
                ", handCards=" + Arrays.toString(handCards) +
                ", sitDownTime=" + sitDownTime +
                ", standUpTime=" + standUpTime +
                ", totalPlayMinutes=" + totalPlayMinutes +
                ", totalWinLoseScore=" + totalWinLoseScore +
                '}';
    }

    /**
     * 增加筹码
     *
     * @param score     操作筹码
     * @param logReason 操作原因
     * @return
     */
    public boolean addPlayScoreStore(double score, LogReasons.CommonLogReason logReason) {
        //筹码是负数
        if (score <= 0) {
            return false;
        }

        //数据处理
        score = NumberUtils.double2Decimal(score);
        logger.debug("玩家添加筹码操作,uid={},score={},reason={},",playerId,score,logReason.reasonText);

        //玩家不存在
        User user = StoredObjManager.hget(RedisConst.USER_INFO.getProfix(), RedisConst.USER_INFO.getField() + playerId, User.class);
        if (user == null) {
            return false;
        }

        double _org = this.playScoreStore == 0 ? user.getMoney() : this.playScoreStore;
        //增加积分
        this.playScoreStore += score;
        if (this.playScoreStore != 0) {
            user.setMoney(this.playScoreStore);
        } else {
            user.setMoney(user.getMoney() + score);
        }
        logger.debug("玩家添加完筹码后的用户信息。 userInfo->" + user.toString());
        StoredObjManager.hset(RedisConst.USER_INFO.getProfix(), RedisConst.USER_INFO.getField() + playerId, user);

        double score4lambda = score;
        executorService.submit(()->{
            //发送日志
            LogService.OBJ.sendMoneyLog(user, _org, user.getMoney(), score4lambda, logReason);
        });

        return true;
    }

    /**
     * 扣除筹码
     *
     * @param score     操作筹码
     * @param logReason 操作原因
     * @return
     */
    public boolean minusPlayScoreStore(double score, LogReasons.CommonLogReason logReason) {
        //筹码是负数
        if (score <= 0 || this.playScoreStore < score) {
            return false;
        }

        //数据处理
        score = NumberUtils.double2Decimal(score);
        logger.debug("玩家添加筹码操作,uid={},score={},reason={},",playerId,score,logReason.reasonText);

        //玩家不存在
        User user = StoredObjManager.hget(RedisConst.USER_INFO.getProfix(), RedisConst.USER_INFO.getField() + playerId, User.class);
        if (user == null) {
            return false;
        }

        double _org = this.playScoreStore;
        //扣除积分
        this.playScoreStore -= score;

        user.setMoney(this.playScoreStore);
//        StoredObjManager.hset(RedisConst.USER_INFO.getProfix(), RedisConst.USER_INFO.getField() + playerId, user);

        double score4lambda = score; //传值给lambda表达式
        executorService.submit(()->{
            //更新数据库信息
            MoneyService.getInstance().updateMoney(user.getId(), this.playScoreStore);
            //发送日志
            LogService.OBJ.sendMoneyLog(user, _org, this.getPlayScoreStore(), score4lambda, logReason);
        });
        return true;
    }

    public String playerToString() {
        return "roomId->" + roomId + ", tableId->" + tableId + ", playerId->" + playerId + ", seatNum->" + seatNum + ", playScoreStore->" + playScoreStore;
    }

    public void setPlayScoreStore(double playScoreStore) {
        this.playScoreStore = NumberUtils.double2Decimal(playScoreStore);
    }

    public void setAlreadyBetScore4Round(double alreadyBetScore4Round) {
        this.alreadyBetScore4Round = NumberUtils.double2Decimal(alreadyBetScore4Round);
    }

    public void setTotalAlreadyBetScore4Hand(double totalAlreadyBetScore4Hand) {
        this.totalAlreadyBetScore4Hand = NumberUtils.double2Decimal(totalAlreadyBetScore4Hand);
    }

    public void setWinLoseScore4Hand(double winLoseScore4Hand) {
        this.winLoseScore4Hand = NumberUtils.double2Decimal(winLoseScore4Hand);
    }

    public void setTotalTakeInScore(double totalTakeInScore) {
        this.totalTakeInScore = NumberUtils.double2Decimal(totalTakeInScore);
    }

    public void setTotalWinLoseScore(double totalWinLoseScore) {
        this.totalWinLoseScore = NumberUtils.double2Decimal(totalWinLoseScore);
    }

    public void setBiggestChipsWon(double biggestChipsWon) {
        this.biggestChipsWon = NumberUtils.double2Decimal(biggestChipsWon);
    }

    public void setChipsWon(double chipsWon) {
        this.chipsWon = NumberUtils.double2Decimal(chipsWon);
    }

    public void setChipsLost(double chipsLost) {
        this.chipsLost = NumberUtils.double2Decimal(chipsLost);
    }

    public void setCurChipsLose(double curChipsLose) {
        this.curChipsLose = NumberUtils.double2Decimal(curChipsLose);
    }

    public double getPlayScoreStore() {
        return NumberUtils.double2Decimal(playScoreStore);
    }

    public double getAlreadyBetScore4Round() {
        return NumberUtils.double2Decimal(alreadyBetScore4Round);
    }

    public double getTotalAlreadyBetScore4Hand() {
        return NumberUtils.double2Decimal(totalAlreadyBetScore4Hand);
    }

    public double getWinLoseScore4Hand() {
        return NumberUtils.double2Decimal(winLoseScore4Hand);
    }

    public double getTotalTakeInScore() {
        return NumberUtils.double2Decimal(totalTakeInScore);
    }

    public double getTotalWinLoseScore() {
        return NumberUtils.double2Decimal(totalWinLoseScore);
    }

    public double getBiggestChipsWon() {
        return NumberUtils.double2Decimal(biggestChipsWon);
    }

    public double getChipsWon() {
        return NumberUtils.double2Decimal(chipsWon);
    }

    public double getChipsLost() {
        return NumberUtils.double2Decimal(chipsLost);
    }

    public double getCurChipsLose() {
        return NumberUtils.double2Decimal(curChipsLose);
    }
}
